<template>
  <n-upload
    ref="upload"
    class="f-ad-upload"
    :action="action"
    :headers="headers"
    :data="getProps.isName ? uploadData : paramsData"
    :disabled="disabled"
    :accept="getProps.params.accept"
    :show-file-list="false"
    :with-credentials="true"
    @before-upload="beforeUpload"
    @finish="onFinish"
    @remove="onRemove"
    @error="onError"
    @change="onProgress"
  >
    <slot />
  </n-upload>
</template>

<script setup lang="ts">
  import { ref, reactive, computed, nextTick, watch } from 'vue'
  import { NUpload, UploadFileInfo, useMessage } from 'naive-ui'
  import { bytesToSize } from '@/utils/index'
  import $store, { useStore } from '@/store'
  import { useRoute } from 'vue-router'
  import { local_storage } from '@/utils/storage'
  import { TOKEN_LOGIN_KEY, TOKEN_STORE_KEY } from '@/enums/cacheEnum'
  import { encode } from 'js-base64'
  import { is99Mall, isHQSC, isHZL, isSaaS } from '@/utils/is'

  const store = useStore()
  const route = useRoute()
  const emit = defineEmits(['before-upload', 'finish', 'error', 'remove', 'progress'])
  const message = useMessage()
  const props = defineProps({
    params: {
      type: Object,
      default: () => ({})
    },
    disabled: {
      type: Boolean,
      default: false
    },
    accountCode: {
      type: String,
      default: ''
    },
    isName: {
      type: Boolean,
      default: false
    }
  })
  const getProps = reactive(props)
  const upload = ref()
  const action = ref(getProps.params.action ? getProps.params.action : `/api/engine/ad/imageVideo/upload`)
  const fileInfo = ref()
  const file = ref()
  const isImage = ref()
  const rules = computed(() => {
    /**
     * 宽高 必须等于或者大于
     * width
     * height
     * 文件大小
     * size (b)
     * **/
    return getProps.params.rules || {}
  })
  const headers = computed(() => {
    const System = is99Mall ? 1 : isHZL ? 2 : isHQSC ? 3 : isSaaS ? 4 : null
    return {
      accessToken: local_storage.get(TOKEN_STORE_KEY) || local_storage.get(TOKEN_LOGIN_KEY),
      'x-system': System
    }
  })
  const paramsData = computed(() => {
    const params = {
      ...getProps.params.data,
      ...fileInfo.value
    }
    return params
  })
  const accountCode = computed(() => {
    return store.state.intelligent.data.accountCode
  })
  const uploadData = ref(paramsData.value)
  watch(
    () => getProps.params.data,
    (val) => {
      if (val) {
        const params = {
          ...val,
          ...fileInfo.value
        }
        if (val?.name) {
          params.name = val.name
        }
        uploadData.value = params
      }
    }
  )
  // 上传文件之前的钩子
  async function beforeUpload({ file }) {
    // 确定文件类型，true 为图片，false 为视频；因为限制了文件选择后缀，所以简单校验一下即可
    const URL = window.URL || window.webkitURL
    const fileBody = file.file
    const src = URL.createObjectURL(fileBody)
    let is = true
    let info = {
      width: '',
      height: ''
    }
    if (fileBody.type.indexOf('image') !== -1) {
      // 图片格式校验
      file.value = new Image()
      file.value.src = src
      info = await validateImage(fileBody, src)
      is = info ? true : Promise.reject()
      if (typeof info === 'object') {
        fileInfo.value = {
          ...uploadData.value,
          imageWidth: info.width,
          imageHeight: info.height,
          accountCode: accountCode.value || getProps.accountCode,
          fileType: 1,
          mediaType: 1,
          fileSize: fileBody.size
        }
      }
    } else if (fileBody.type.indexOf('video') !== -1) {
      // 视频校验
      info = await validateVideo(fileBody, src)
      is = info ? true : Promise.reject()
      fileInfo.value = {
        accountCode: accountCode.value || getProps.accountCode,
        fileType: 2,
        mediaType: 2
      }
    }
    if (is) {
      // 开始上传
      emit('before-upload', file)
    }
    return is
  }
  // 文件上传成功时的钩子
  function onFinish({ file, event }: { file: UploadFileInfo; event?: ProgressEvent }) {
    console.log('上传成功！', file, JSON.parse((event?.target as XMLHttpRequest).response))
    // 上传完成
    const { success, data, msg } = JSON.parse((event?.target as XMLHttpRequest).response)
    const messageText = msg || '上传出错，请校对是否符合上传要求'
    // 此处可能需要根据 response 的值，校验接口是否成功
    if (success && data && (data.media_id || data.mediaId || data.imageId || data.fileUrl)) {
      emit('finish', data, file)
    } else {
      message.error(messageText)
      emit('error', ...arguments)
    }
  }
  // 文件列表移除文件时的钩子
  function onRemove() {
    emit('remove', ...arguments)
  }
  // 文件上传失败时的钩子
  function onError() {
    console.log('上传失败')
    message.error('抱歉，上传失败；请检查文件是否超出限制~', { duration: 5 * 1000 })
    emit('error', ...arguments)
  }
  // 文件上传时的钩子
  function onProgress(event:any) {
    console.log('上传进度--> ', event)
    if (uploadData.value.name) {
      uploadData.value = {
        ...uploadData.value,
        ...fileInfo.value
      }
    }
    emit('progress', ...arguments)
  }
  // 校验图片
  function validateImage(file:any, src:string) {
    return new Promise((resolve, reject) => {
      if (rules.value.size && file.size > rules.value.size) {
        message.warning(`请上传≤${bytesToSize(rules.value.size / 1024)}的素材`)
        return resolve(false)
      }
      // 尺寸验证
      file.value = new Image()
      file.value.src = src
      file.value.onload = () => {
        // 图片尺寸校验容错5像素
        if (!rules.value.ratio) {
          const width = Math.abs(rules.value.width - file.value.width)
          const height = Math.abs(rules.value.height - file.value.height)
          if (rules.value.width && (width > 5 || height > 5)) {
            message.warning(`请上传推荐尺寸 ${rules.value.width}px X ${rules.value.height}px 的素材`)
            return resolve(false)
          }
          return resolve({
            width: file.value.width,
            height: file.value.height
          })
        } else {
          if (
            file.value.width / file.value.height !== 1280 / 720 &&
            file.value.width / file.value.height !== 720 / 1280
          ) {
            message.warning(`请上传推荐尺寸 1280px*720px或者720px*1280px 的素材`)
            return resolve(false)
          }
          const width1 = Math.abs(720 - file.value.width)
          const width2 = Math.abs(1280 - file.value.width)
          const height1 = Math.abs(1280 - file.value.height)
          const height2 = Math.abs(720 - file.value.height)
          if (width1 > 560 && width2 > 5) {
            message.warning(`请上传推荐尺寸 1280px*720px或者720px*1280px 的素材`)
            return resolve(false)
          }
          if (height2 > 560 && height1 > 5) {
            message.warning(`请上传推荐尺寸 1280px*720px或者720px*1280px 的素材`)
            return resolve(false)
          }
          return resolve({
            width: file.value.width,
            height: file.value.height
          })
        }
      }
    })
  }
  // 校验视频
  function validateVideo(file:any, src:string) {
    return new Promise(async (resolve, reject) => {
      // 不做任何校验
      if (!rules.value) return resolve(true)
      // 开始做校验
      file.value = src
      if (file.size > rules.value.size) {
        message.warning(`请上传≤${bytesToSize(rules.value.size / 1024)}的素材`)
        return resolve(false)
      }
      if (!rules.value.ratio) {
        if (rules.value.width && rules.value.height) {
          const { width, height } = await canplay(file) as any
          // 视频尺寸校验容错5像素
          if (Math.abs(width - rules.value.width) > 5 || Math.abs(height - rules.value.height) > 5) {
            message.warning(`请上传推荐尺寸 ${rules.value.width}px X ${rules.value.height}px 的素材`)
            return resolve(false)
          } else {
            return resolve(true)
          }
        }
        return resolve(true)
      } else {
        const { width, height } = await canplay(file) as any
        if (width / height !== 1280 / 720 && width / height !== 720 / 1280) {
          message.warning(`请上传推荐尺寸 1280px*720px或者720px*1280px 的素材`)
          return resolve(false)
        }
        const width1 = Math.abs(720 - width)
        const width2 = Math.abs(1280 - width)
        const height1 = Math.abs(1280 - height)
        const height2 = Math.abs(720 - height)
        if (width1 > 560 && width2 > 5) {
          message.warning(`请上传推荐尺寸 1280px*720px或者720px*1280px 的素材`)
          return resolve(false)
        }
        if (height2 > 560 && height1 > 5) {
          message.warning(`请上传推荐尺寸 1280px*720px或者720px*1280px 的素材`)
          return resolve(false)
        }
        return resolve(true)
      }
    })
  }
  // 获取视频信息
  function canplay(file:any) {
    return new Promise(async (resolve, reject) => {
      const URL = window.URL || window.webkitURL
      const src = URL.createObjectURL(file)
      const video = document.createElement('video')
      video.src = src
      video.oncanplay = () => {
        resolve({
          width: video.videoWidth,
          height: video.videoHeight
        })
      }
    })
  }
  function clear() {
    upload.value?.clear()
  }
  function submit() {
    upload.value?.submit()
  }
  defineExpose({
    clear,
    submit
  })
</script>

<style></style>
